#coding=utf-8
from __future__ import with_statement
#   Author:Zhangbo
#   Email:zhangboks@gmail.com
#   QQ(If you have):513364476

import collections
import errno
import logging
import socket
import sys
import os
import re
import cgi
import functools
import cPickle as pickle

try:
    from netMinc import buffer, reactor
except ImportError:
    import init_netminc

# class AsyncConnection(object):
#     def __init__(self, ip, port, callback_write, callback_read):
#         self.callback_write = callback_write
#         self.callback_read = callback_read
#         self.sock = None
#         self._buffer = None

#     def connect_callback(self):
#         self._buffer.write()


class PickleRpcConnection(object):
    def __init__(self, _buffer, rpc_application):
        self._buffer = _buffer
        self._rpc_application = rpc_application
        self._error_code = -2
        self._read_head_data()

    def _read_head_data(self):
        self._buffer.read_until("\r\n\r\n", self._on_read_head)

    def _on_read_head(self, data):
        content_length, version, method = data.split(' ')
        if not content_length or not version or not method:
            self.close()
        self._buffer.read_until(content_length, self._on_read_body)

    def _on_read_body(self, data):
        if not data or not isinstance(data, dict):
            self.close()
        else:
            self._rpc_application(self, data)

    def _dump(self, data):
        return pickle.dump(data)

    def _load(self, data):
        return pickle.load(data)

    def _handle_error(self):
        if self._error_code == 0:
            self._buffer.write("#Error#:PARAMS ERROR", self._buffer.close)
        elif error_code == 1:
            self._buffer.write("#Error#:BODY ERROR", self._buffer.close)
        else:
            self._buffer.write("#Error#:UNKNOWN ERROR", self._buffer.close)

    def close(self):
        self._handle_error()

    def write(self, data, callback=None):
        if not callback:
            self._buffer.write(data)
        else:
            self._buffer.write(data, callback)


class ApplicationBase(object):

    def __init__(self, connection, **data):
        self.connection = connection
        self._data = data
        self._callable_object_ = dict()
        self.setting()
        self.handle_data()

    def handle_data(self):
        name = self._data.get("name", None)
        data = self._data.get("data", '')
        if not name:
            return
        if name in self._callable_object_:
            self.connection.write(self._callable_object_[name](data))

    def setting(self):
        raise NotImplementedError

    def add_handle(self, app_name, callback):
        if not isinstance(callback, function):
            logging.error("Error: Add callback handle error, callback not callable")
        if app_name in self._callable_object_:
            logging.warning("Warning: Callback exists in calllbacks set, update default")
        self._callable_object_[app_name] = callback


class PickleRpcServer(object):
    def __init__(self, app):
        self.app = app
        self._reactor = None

    def connection_ready(sock, fd, events):
        while 1:
            try:
                con, add = sock.accept()
            except socket.error, e:
                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    return
            if con:
                con.setblocking(0)
                print  con, add
                _Buf = buffer.Buffer(con)
                PickleRpcConnection(_Buf, self.app)

    def run(self, port=1115):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.setblocking(0)
            self.sock.bind(("", port))
            self.sock.listen(512)
        except OSError, IOError:
            logging.error("Server start error")
        finally:
            if self.sock:
                self.sock.close()

        self._reactor = reactor.Reactor.instance()
        self._reactor.add_handler(self.sock.fileno(), functools.partial(self.connection_ready, self.sock), self._reactor.READEV)
        self._reactor.run()

    def close(self):
        if self.sock:
            self.sock.close()
            self.sock = None
        if self._reactor:
            self._reactor.close()

if __name__ == "__main__":
    class APP(ApplicationBase):
        def setting(self):
            self.add_handle('mine', self.handle_mine)

        def handle_mine(self, data):
            print data
            return data

    Server = PickleRpcServer(APP)
    Server.run(port=1115)
